package org.infodavid.common.impl;

import java.util.ArrayList;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.infodavid.common.IMessages;

/**
 * The Class MessagesImpl.
 */
public final class MessagesImpl implements IMessages {

	/** LOG. */
	private static final Log LOG = LogFactory.getLog(IMessages.class);

	/** The Constant formatSpecifier. */
	private static final String VARIABLE_REGEX = "%(\\w)+\\b";

	/** The fs pattern. */
	private static final Pattern VARIABLE_PATTERN = Pattern.compile(VARIABLE_REGEX);

	private final String basename;

	/**
	 * Constructor.
	 * @param basename The base name of the embedded resource bundle
	 */
	protected MessagesImpl(final String basename) {
		super();

		this.basename = basename;
	}

	/**
	 * Parses the.
	 * @param value the value
	 * @return the string[]
	 */
	private String[] parse(final String value) {
		final ArrayList<String> al = new ArrayList<String>();
		final Matcher matcher = VARIABLE_PATTERN.matcher(value);
		String string;
		int i = 0;

		while (i < value.length() && matcher.find(i)) {
			if (matcher.start() != i) {
				string = value.substring(matcher.start(), matcher.end());

				if (string.charAt(0) == '%') {
					al.add(string.substring(1));
				}
			}

			i = matcher.end();
		}

		return al.toArray(new String[al.size()]);
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.IMessages#getString(java.lang.String)
	 */
	public String getString(final String key) {
		return getString(key, Locale.getDefault());
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.IMessages#getString(java.lang.String, java.util.Locale)
	 */
	public String getString(final String key, final Locale locale) {
		final ResourceBundle resource =
		    ResourceBundle.getBundle(basename, locale == null ? Locale.getDefault() : locale);
		String value;

		try {
			value = resource.getString(key);
		}
		catch (final MissingResourceException e) {
			LOG.warn("Message not found for key: " + key);

			value = "";
		}

		if (value == null) {
			if (LOG.isDebugEnabled()) {
				LOG.debug("Resource not defined for key: " + key);
			}
		}
		else if (value.trim().length() > 0) {
			final String[] keys = parse(value);

			if (!(keys == null || keys.length == 0)) {
				String replacement;

				for (int i = 0; i < keys.length; i++) {

					if (keys[i] == null) {
						if (LOG.isDebugEnabled()) {
							LOG.debug("key is null at index: " + i + ", setting replacement as empty.");
						}

						replacement = "";
					}
					else {
						replacement = Matcher.quoteReplacement(getString(keys[i]));
					}

					if (replacement.length() > 0) {
						value = value.replaceAll(VARIABLE_SYMBOL + keys[i], replacement);
					}
					else {
						value = value.replaceAll(' ' + VARIABLE_SYMBOL + keys[i], replacement);
						value = value.replaceAll(VARIABLE_SYMBOL + keys[i] + ' ', replacement);
						value = value.replaceAll(VARIABLE_SYMBOL + keys[i], replacement);
					}
				}
			}
		}

		return value;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.IMessages#getString(java.lang.String, java.lang.Object[])
	 */
	public String getString(final String key, final Object... variables) {
		return getString(key, Locale.getDefault(), variables);
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.IMessages#getString(java.lang.String, java.util.Locale,
	 * java.lang.Object[])
	 */
	public String getString(final String key, final Locale locale, final Object... variables) {
		final ResourceBundle resource =
		    ResourceBundle.getBundle(basename, locale == null ? Locale.getDefault() : locale);
		String value = resource.getString(key);

		if (value == null) {
			if (LOG.isDebugEnabled()) {
				LOG.debug("Resource not defined for key: " + key);
			}
		}
		else if (!(value.trim().length() == 0 || variables == null || variables.length == 0)) {
			String replacement;

			for (int i = 0; i < variables.length; i++) {
				if (variables[i] == null) {
					if (LOG.isDebugEnabled()) {
						LOG.debug("Variable is null at index: " + i);
					}

					replacement = "";
				}
				else {
					replacement = Matcher.quoteReplacement(variables[i].toString());
				}

				if (replacement.length() > 0) {
					value = value.replaceAll(VARIABLE_SYMBOL + i, replacement);
				}
				else {
					value = value.replaceAll(' ' + VARIABLE_SYMBOL + i, replacement);
					value = value.replaceAll(VARIABLE_SYMBOL + i + ' ', replacement);
					value = value.replaceAll(VARIABLE_SYMBOL + i, replacement);
				}
			}
		}

		return value;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.IMessages#getLong(java.lang.String)
	 */
	public Long getLong(final String key) {
		final String string = getString(key);
		Long value = null;

		if (string != null) {
			if (string.trim().length() == 0) {
				LOG.debug("Resource defined is empty for key: " + key);
			}
			else {
				value = Long.valueOf(string);
			}
		}

		return value;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.IMessages#getDouble(java.lang.String)
	 */
	public Double getDouble(final String key) {
		final String string = getString(key);
		Double value = null;

		if (string != null) {
			if (string.trim().length() == 0) {
				LOG.debug("Resource defined is empty for key: " + key);
			}
			else {
				value = Double.valueOf(string);
			}
		}

		return value;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.IMessages#getBoolean(java.lang.String)
	 */
	public Boolean getBoolean(final String key) {
		String string = getString(key);
		Boolean value = null;

		if (string != null) {
			if (string.trim().length() == 0) {
				LOG.debug("Resource defined is empty for key: " + key);
			}
			else {
				string = string.toLowerCase();
				value =
				    Boolean.valueOf("true".equals(string) || "yes".equals(string) || "on".equals(string)
				        || "1".equals(string));
			}
		}

		return value;
	}
}
